package com.ssc.chain.tcfp.core.client;



import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.util.Base64;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author mingjian
 * @version 1.0
 * Create by 2025/7/10 17:23
 */
public class TcfpUploadTemplate {

    private final TcfpClientConfig config;
    private final ExecutorService executorService;
    private final OkHttpClient httpClient;

    public TcfpUploadTemplate(TcfpClientConfig config) {
        this.config = config;
        this.executorService = Executors.newFixedThreadPool(4);
        this.httpClient = new OkHttpClient.Builder()
                .connectTimeout(config.getConnectTimeout())
                .retryOnConnectionFailure(true)
                .build();
    }

    public void upload(TcfpUploadRequest request, UploadListener listener) {
        executorService.submit(() -> {
            try {
                internalUpload(request, listener);
            } catch (Exception e) {
                listener.onError(request, e);
            }
        });
    }

    private void internalUpload(TcfpUploadRequest request, UploadListener listener) throws Exception {
        long totalBytes = request.isStreamUpload() ? request.getFileLength() : request.getFile().length();
        listener.onStart(request, totalBytes);

        // Step 1: 创建 upload 资源
        Request.Builder builder = new Request.Builder()
                .url(request.getTargetUrl())
                .post(RequestBody.create(new byte[0]))
                .header("Tcfp-Resumable", "1.0.0")
                .header("Upload-Length", String.valueOf(totalBytes))
                .header("Upload-Metadata", buildMetadata(request));

        if (request.getToken() != null) {
            builder.header("Authorization", "Bearer " + request.getToken());
        }

        Response res = httpClient.newCall(builder.build()).execute();
        if (!res.isSuccessful()) throw new IOException("创建 upload 资源失败：" + res);

        String uploadUrl = res.header("Location");
        if (!uploadUrl.startsWith("http")) {
            // 相对地址补全
            String base = request.getTargetUrl();
            uploadUrl = base + (base.endsWith("/") ? "" : "/") + uploadUrl;
        }

        // Step 2: 开始分块上传 PATCH
        long uploaded = 0;
        byte[] buffer = new byte[(int) config.getChunkSize().toBytes()];
        try (InputStream in = request.isStreamUpload() ?
                request.getFileStream() :
                new BufferedInputStream(Files.newInputStream(request.getFile().toPath()))) {

            while (true) {
                int read = in.read(buffer);
                if (read == -1) break;

                boolean success = false;
                int attempt = 0;

                while (!success && attempt < config.getRetry()) {
                    attempt++;

                    RequestBody chunk = RequestBody.create(buffer);
                    Request patchReq = new Request.Builder()
                            .url(uploadUrl)
                            .patch(chunk)
                            .header("Tus-Resumable", "1.0.0")
                            .header("Upload-Offset", String.valueOf(uploaded))
                            .build();

                    try (Response patchRes = httpClient.newCall(patchReq).execute()) {
                        if (patchRes.isSuccessful()) {
                            uploaded += read;
                            listener.onProgress(request, uploaded, totalBytes);
                            success = true;
                        } else {
                            System.err.println("第 " + attempt + " 次 PATCH 失败: " + patchRes);
                        }
                    }
                }

                if (!success) throw new IOException("分块上传失败超出重试次数");
            }
        }

        listener.onSuccess(request);
    }

    private String buildMetadata(TcfpUploadRequest req) {
        StringBuilder sb = new StringBuilder();

        String fileName = req.getFileNameSafe();
        sb.append("filename ")
                .append(Base64.getEncoder().encodeToString(fileName.getBytes()));

        if (req.getMetadata() != null) {
            sb.append(",").append(req.getMetadata());
        }

        return sb.toString();
    }
}